package com.yuaihen.camera.camera;

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Point;
import android.graphics.SurfaceTexture;
import android.hardware.Camera;
import android.opengl.GLES20;
import android.opengl.GLSurfaceView;
import android.util.AttributeSet;
import android.util.Log;
import android.view.SurfaceHolder;

import com.yuaihen.camera.filters.BaseFilter;
import com.yuaihen.camera.gpuimage.GPUImageNativeLibrary;
import com.yuaihen.camera.interfaces.FileCallback;
import com.yuaihen.camera.interfaces.FilteredBitmapCallback;
import com.yuaihen.camera.utils.FilterFactory;

import java.io.File;
import java.util.LinkedList;
import java.util.Queue;
import java.util.concurrent.Semaphore;

import javax.microedition.khronos.egl.EGLConfig;
import javax.microedition.khronos.opengles.GL10;

/**
 * Created by Yuaihen.
 * on 2020/7/22
 * 相机预览View SurfaceView
 */
public class GLCameraView extends GLSurfaceView {
    private static final String TAG = "GLCameraView";
    private float[] mSTMatrix = new float[16];
    private Context context;
    /**
     * 滤镜类型
     */
    private FilterFactory.FilterType type;
    private GLRenderer renderer;
    private BaseFilter mCurrentFilter;
    private CameraHelper mCameraHelper;
    private int mTextureId;
    private SurfaceTexture mSurfaceTexture;
    private FileCallback mFileCallback;
    private File mOutputFile;

    public GLCameraView(Context context) {
        super(context);
        init(context);
    }

    public GLCameraView(Context context, AttributeSet attrs) {
        super(context, attrs);
        init(context);
    }

    private void init(Context context) {
        this.context = context;
        setEGLContextClientVersion(2);
        //默认美颜滤镜
        type = FilterFactory.FilterType.Beauty;
        renderer = new GLRenderer(this, type);
        setRenderer(renderer);
        setRenderMode(RENDERMODE_WHEN_DIRTY);
    }

    public class GLRenderer implements Renderer, SurfaceTexture.OnFrameAvailableListener, CameraListener {
        GLSurfaceView surfaceView;
        private final Queue<Runnable> runOnDraw;
        private final Queue<Runnable> runOnDrawEnd;

        public GLRenderer(GLSurfaceView surfaceView, FilterFactory.FilterType type) {
            this.surfaceView = surfaceView;
            //录像用到的
            //            hwRecorderWrapper = new HWRecorderWrapper(surfaceView.getContext());
            //            mOutputFile = FileUtils.createVideoFile();
            //            mRecordingEnabled = false;

            //创建相机辅助类
            mCameraHelper = new CameraHelper.Builder()
                    .previewViewSize(new Point(surfaceView.getMeasuredWidth(), surfaceView.getMeasuredHeight()))
                    .previewOn(surfaceView)
                    //                    .rotation(getwindow)
                    .isMirror(false)
                    .cameraListener(this)
                    .build();

            mCurrentFilter = FilterFactory.createFilter(context, type);
            runOnDraw = new LinkedList<>();
            runOnDrawEnd = new LinkedList<>();
        }

        @Override
        public void onSurfaceCreated(GL10 gl, EGLConfig config) {

        }

        @Override
        public void onSurfaceChanged(GL10 gl, int width, int height) {
            GLES20.glViewport(0, 0, width, height);
            mCurrentFilter.createProgram();
            mCurrentFilter.onInputSizeChanged(getWidth(), getHeight());

            mTextureId = BaseFilter.bindTexture();
            mSurfaceTexture = new SurfaceTexture(mTextureId);
            mSurfaceTexture.setOnFrameAvailableListener(this);

            //开启相机和预览
            mCameraHelper.openCamera();
            mCameraHelper.startPreview(mSurfaceTexture);
        }

        /**
         * 关于预览出现镜像，旋转等问题，有两种方案:
         * 1.在相机预览的地方进行调整
         * 2.通过opengl的矩阵变换在绘制的时候进行调整
         * 这里我采用了前者
         */
        @Override
        public void onDrawFrame(GL10 gl) {
            runAll(runOnDraw);
            mSurfaceTexture.updateTexImage();
            //录像相关
            //            if (mRecordingEnabled) {
            //                if (state == STATE_OFF) {
            //                    hwRecorderWrapper.start(
            //                            mCameraHelper.fitHeight,
            //                            mCameraHelper.fitWidth,
            //                            DEFAULT_BITRATE, mSampleRate,
            //                            mChannels, mOutputFile.getAbsolutePath(),
            //                            EGL14.eglGetCurrentContext());
            //
            //                    state = STATE_ON;
            //                } else {
            //                    //already on
            //                    //do nothing
            //                }
            //
            //            } else {
            //                if (state == STATE_ON) {
            //                    hwRecorderWrapper.stop();
            //                    state = STATE_OFF;
            //                    if (mFileCallback != null) {
            //                        mFileCallback.onData(mOutputFile);
            //                    }
            //                } else {
            //                    //already off
            //                    //do noting
            //                }
            //            }

            //            hwRecorderWrapper.onFrameAvailable(mTextureId, mSurfaceTexture);
            mSurfaceTexture.getTransformMatrix(mSTMatrix);
            mCurrentFilter.draw(mTextureId, mSTMatrix);
            runAll(runOnDrawEnd);
        }

        @Override
        public void onFrameAvailable(SurfaceTexture surfaceTexture) {
            surfaceView.requestRender();
        }


        void runOnDraw(final Runnable runnable) {
            synchronized (runOnDraw) {
                runOnDraw.add(runnable);
            }
        }

        void runOnDrawEnd(final Runnable runnable) {
            synchronized (runOnDrawEnd) {
                runOnDrawEnd.add(runnable);
            }
        }

        private void runAll(Queue<Runnable> queue) {
            synchronized (queue) {
                while (!queue.isEmpty()) {
                    queue.poll().run();
                }
            }
        }

        @Override
        public void onCameraOpened(Camera camera, int cameraId, int displayOrientation, boolean isMirror) {
            Log.i(TAG, "onCameraOpened: $cameraId  $displayOrientation $isMirror");
            //            previewSize = camera.getParameters().getPreviewSize();
        }

        @Override
        public void onPreview(byte[] data, Camera camera) {
            Log.i(TAG, "onPreview: ");
        }

        @Override
        public void onCameraClosed() {
            Log.i(TAG, "onCameraClosed: ");
        }

        @Override
        public void onCameraError(Exception e) {
            Log.i(TAG, "onCameraError: " + e.getMessage());
        }

        @Override
        public void onCameraConfigurationChanged(int cameraID, int displayOrientation) {
            Log.i(TAG, "onCameraConfigurationChanged:" + cameraID + " " + displayOrientation);
        }
    }

    /**
     * 相机调用拍照返回当前预览界面的Bitmap
     */
    public void takePicture(final FilteredBitmapCallback filteredBitmapCallback) {
        //获取当前的bitmap
        try {
            filteredBitmapCallback.onData(capture());
        } catch (InterruptedException e) {
            Log.v(TAG, e.getMessage() + "");
        }
    }


    public void changeRecordingState(boolean mRecordingEnabled) {
        //        this.mRecordingEnabled = mRecordingEnabled;
    }

    public void switchCamera() {
        mCameraHelper.switchCamera();
        //        Log.d(TAG, "switchCamera:" + Thread.currentThread());
    }

    public void updateFilter(final FilterFactory.FilterType type) {
        this.type = type;

        renderer.runOnDraw(() -> {
            mCurrentFilter.releaseProgram();
            mCurrentFilter = FilterFactory.createFilter(context, type);

            //调整预览画面
            mCurrentFilter.createProgram();
            mCurrentFilter.onInputSizeChanged(getWidth(), getHeight());
            //调整录像画面
            //            hwRecorderWrapper.updateFilter(type);

            //            Log.d(TAG, "updateFilter:" + Thread.currentThread());
        });
    }

    /**
     * 捕获当前显示View的大小并创建Bitmap返回
     */
    private Bitmap capture() throws InterruptedException {
        final Semaphore waiter = new Semaphore(0);

        final int width = getMeasuredWidth();
        final int height = getMeasuredHeight();

        // Take picture on OpenGL thread
        final Bitmap resultBitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);

        renderer.runOnDrawEnd(() -> {
            GPUImageNativeLibrary.adjustBitmap(resultBitmap);
            waiter.release();

            //            Log.d(TAG, "current thread is:" + Thread.currentThread().getName());
        });

        requestRender();
        waiter.acquire();
        return resultBitmap;
    }


    public void setOuputMP4File(File file) {
        this.mOutputFile = file;
    }

    public void setRecordFinishedListnener(FileCallback fileCallback) {
        this.mFileCallback = fileCallback;
    }

    /**
     * 关闭或开启美颜
     */
    public void enableBeauty(boolean enableBeauty) {
        if (enableBeauty) {
            type = FilterFactory.FilterType.Beauty;
        } else {
            type = FilterFactory.FilterType.Original;
        }
        updateFilter(type);
    }

    /**
     * 0~1
     * 更改美颜磨皮参数
     */
    public void setBeautyLevel(float beautyLevel) {
        //        if (mCurrentFilter instanceof BeautyFilter) {
        //            ((BeautyFilter) mCurrentFilter).setSmoothOpacity(beautyLevel);
        //            hwRecorderWrapper.changeBeautyLevel(beautyLevel);
        //        }
    }

    public CameraHelper getCameraHelper() {
        return mCameraHelper;
    }

    public SurfaceTexture getSurfaceTexture() {
        return mSurfaceTexture;
    }


    @Override
    public void surfaceDestroyed(SurfaceHolder holder) {
        super.surfaceDestroyed(holder);
        if (mCameraHelper != null) {
            mCameraHelper.release();
            mCameraHelper = null;
        }
    }

}
